/*
   EddystoneURL beacon by BeeGee
   EddystoneURL frame specification https://github.com/google/eddystone/blob/master/eddystone-url/README.md

*/

/*
   Create a BLE server that will send periodic Eddystone URL frames.
   The design of creating the BLE server is:
   1. Create a BLE Server
   2. Create advertising data
   3. Start advertising.
   4. wait
   5. Stop advertising.
   6. deep sleep

*/
#include "sys/time.h"

#include <Arduino.h>
// 任意支持BLE的设备最终都抽象为BLEDevice
#include "BLEDevice.h"
#include "BLEUtils.h"
// 使用Beacon功能
#include "BLEBeacon.h"
// 使用广播功能
#include "BLEAdvertising.h"
// 使用Google EddystoneURL协议
#include "BLEEddystoneURL.h"
// 使用睡眠功能
#include "esp_sleep.h"

// 睡眠间隔
#define GPIO_DEEP_SLEEP_DURATION 5     // sleep x seconds and then wake up
// 使用RTC记录信息
RTC_DATA_ATTR static time_t last;    // remember last boot in RTC Memory
RTC_DATA_ATTR static uint32_t bootcount; // remember number of boots in RTC Memory

// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
// 定义一个广播对象入口
BLEAdvertising *pAdvertising;
struct timeval now;
// 定义个Beacon服务ID
#define BEACON_UUID "8ec76ea3-6668-48da-9866-75be8bc86f4d" // UUID 1 128-Bit (may use linux tool uuidgen or random numbers via https://www.uuidgenerator.net/)

// eddystone_url 的前缀
static const char *eddystone_url_prefix_subs[] = {
  "http://www.",
  "https://www.",
  "http://",
  "https://",
  "urn:uuid:",
  NULL
};

// eddystone_url 的后缀
static const char *eddystone_url_suffix_subs[] = {
  ".com/",// 0x00
  ".org/",// 0x01
  ".edu/",// 0x02
  ".net/",// 0x03
  ".info/",// 0x04
  ".biz/",// 0x05
  ".gov/",// 0x06
  ".com",// 0x07
  ".org",// 0x08
  ".edu",// 0x09
  ".net",// 0x0A
  ".info",// 0x0B
  ".biz",// 0x0C
  ".gov",// 0x0D
  NULL
};

static int string_begin_with(const char *str, const char *prefix)
{
  int prefix_len = strlen(prefix);
  if (strncmp(prefix, str, prefix_len) == 0)
  {
    return prefix_len;
  }
  return 0;
}

// 配置Beacon
void setBeacon()
{
  // 构建广播数据
  BLEAdvertisementData oAdvertisementData = BLEAdvertisementData();
  BLEAdvertisementData oScanResponseData = BLEAdvertisementData();

  const char url[] = "http://dpjcn";

  int scheme_len, ext_len = 1, i, idx, url_idx;
  char *ret_data;
  int url_len = strlen(url);
  // 申请最大内存长度
  ret_data = (char *)calloc(1, url_len + 13);
  // 构造广播数据
  // Type = 0x01 Flags 
  ret_data[0] = 2;   // Len
  ret_data[1] = 0x01;  // Type Flags
  ret_data[2] = 0x06;  // GENERAL_DISC_MODE 0x02 | BR_EDR_NOT_SUPPORTED 0x04
  // Type = 0x03 完整的 16 bit UUID
  ret_data[3] = 3;   // Len
  ret_data[4] = 0x03;  // Type 16-Bit UUID
  ret_data[5] = 0xAA;  // Eddystone UUID 2 -> 0xFEAA LSB
  ret_data[6] = 0xFE;  // Eddystone UUID 1 MSB
  // Type = 0x16 UUID + ServiceData（构造Eddystone协议）
  ret_data[7] = 19; // Length of Beacon Data
  ret_data[8] = 0x16;  // Type Service Data
  ret_data[9] = 0xAA;  // Eddystone UUID 2 -> 0xFEAA LSB
  ret_data[10] = 0xFE; // Eddystone UUID 1 MSB
  ret_data[11] = 0x10; // Eddystone Frame Type
  ret_data[12] = 0xF4; // Beacons TX power at 0m

  i = 0, idx = 13, url_idx = 0;

  //构造url前缀
  scheme_len = 0;
  while (eddystone_url_prefix_subs[i] != NULL)
  {
    if ((scheme_len = string_begin_with(url, eddystone_url_prefix_subs[i])) > 0)
    {
      Serial.printf("pre idx %d %s\n", i, eddystone_url_prefix_subs[i]);
      ret_data[idx] = i;
      idx++;
      url_idx += scheme_len;
      break;
    }
    i++;
  }
  Serial.printf("url_idx %d\n", url_idx);
  // 构造Url后缀
  while (url_idx < url_len)
  {
    i = 0;
    ret_data[idx] = url[url_idx];
    ext_len = 1;
    while (eddystone_url_suffix_subs[i] != NULL)
    {
      if ((ext_len = string_begin_with(&url[url_idx], eddystone_url_suffix_subs[i])) > 0)
      {
        Serial.printf("suff idx %d %s\n", i, eddystone_url_suffix_subs[i]);
        ret_data[idx] = i;
        break;
      }
      else
      {
        ext_len = 1; //inc 1
      }
      i++;
    }
    url_idx += ext_len;
    idx++;
  }
  // 为什么减掉8 因为idx从13开始 所以是- 13 + 5 = -8
  Serial.printf("final idx %d\n", idx);
  ret_data[7] = idx - 8;
  // 注意 如果这里的总长度超过了广播包的大小 那就无法收到
  Serial.printf("struct size %d url size %d reported len %d\n",
                url_len + 13,
                url_len, ret_data[7]);

  Serial.printf("URL in data %s\n", &ret_data[13]);

  std::string eddyStoneData(ret_data);
  
  // 设置广播数据
  oAdvertisementData.addData(eddyStoneData);
  oScanResponseData.setName("URLBeacon");
  pAdvertising->setAdvertisementData(oAdvertisementData);
  pAdvertising->setScanResponseData(oScanResponseData);
}

void setup()
{

  Serial.begin(115200);
  gettimeofday(&now, NULL);

  Serial.printf("start ESP32 %d\n", bootcount++);

  Serial.printf("deep sleep (%lds since last reset, %lds since last boot)\n", now.tv_sec, now.tv_sec - last);

  last = now.tv_sec;

  // Create the BLE Device
  BLEDevice::init("URLBeacon");

  BLEDevice::setPower(ESP_PWR_LVL_N12);

  // Create the BLE Server
  // BLEServer *pServer = BLEDevice::createServer(); // <-- no longer required to instantiate BLEServer, less flash and ram usage
  // 获取广播对象
  pAdvertising = BLEDevice::getAdvertising();

  setBeacon();
  pAdvertising->setAdvertisementType(ADV_TYPE_NONCONN_IND);
  // 启动广播
  pAdvertising->start();
  Serial.println("Advertizing started...");
  delay(10000);
  pAdvertising->stop();
  Serial.printf("enter deep sleep\n");
   // 进入休眠
  esp_deep_sleep(1000000LL * GPIO_DEEP_SLEEP_DURATION);
  Serial.printf("in deep sleep\n");
}

void loop()
{
}
